package com.ihr360.payroll.service.salarytasklet.step.calculator;

import com.ihr360.alps.api.ServiceProvider;
import com.ihr360.attendance.AttendancePeriodReport;
import com.ihr360.common.DateRange;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.StringUtils;
import com.ihr360.commons.model.meta.table.DynamicCell;
import com.ihr360.commons.model.type.FieldSource;
import com.ihr360.expr.engine.VariableID;
import com.ihr360.payroll.calc.StaffSalaryCalcPeriod;
import com.ihr360.payroll.calc.StaffSalaryCalcPeriodTerm;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.task.period.StaffPayPeriodTermCalcContext;
import com.ihr360.payroll.model.dto.salary.task.period.embeddable.StaffSalaryCalcContext;
import com.ihr360.payroll.model.dto.salary.task.period.embeddable.StaffSalarySnapshot;
import com.ihr360.payroll.service.salarytasklet.SalaryCalcJobConfig;
import com.ihr360.sharedservice.model.WorkingCalendar;
import com.ihr360.validation.ValidationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author davidwei
 *         员工薪资计算核心逻辑
 */
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class SalaryContextSalaryTermPayConvertor {
    final Logger logger = LoggerFactory.getLogger(SalaryContextSalaryTermPayConvertor.class.getSimpleName());

    private final PersonalCalcLog personalCalcLog = new PersonalCalcLog();

    private Map<String, SalaryField> salaryFieldMap;

    private Set<String> basedOnTermFields;

    /**
     * 保存输入类型的薪资项目取值
     */
    private Map<String, DynamicCell> inputData;

    private String planName;

    /**
     * 保存引用类型(外部服务)的薪资项目取值
     */

    private Map<String, Map<String, Object>> referenceData;

    /**
     * 保存员工的分段薪资薪资
     */
    private StaffSalaryCalcPeriod staffPeriodSalary;

    /**
     * 员工月度考勤报告(在多批次计算的时候，这个报告其实是批次区间内的报告，不是一个月的)
     */
    private AttendancePeriodReport attendanceMonthlyReport;

    /**
     * 员工分段考勤数据
     */
    private List<AttendancePeriodReport> attendancePeriodReports;

    private StaffSalarySnapshot basedOnTermSalary;

    /**
     * 工作日历
     */

    private WorkingCalendar workingCalendar;

    private boolean isSamePeriod = true;

    /**
     * 是否只要一個階段
     */
    private boolean isSinglePhase = true;

    /**
     * 是否是單批次計算
     */
    private boolean isSingleTerm = true;


    private DateRange payrollPriod;

    private DateRange attendancePeriod;


    public void setIsSinglePhase(boolean isSinglePhase) {
        this.isSinglePhase = isSinglePhase;
    }

    public void setIsSingleTerm(boolean isSingleTerm) {
        this.isSingleTerm = isSingleTerm;
    }

    public void setPayRollPeriod(DateRange dateRange) {
        this.payrollPriod = dateRange;
    }

    public void setAttendancePeriod(DateRange dateRange) {
        this.attendancePeriod = dateRange;
    }

    public void setSalaryFieldMap(Map<String, SalaryField> salaryFieldMap) {
        this.salaryFieldMap = salaryFieldMap;
    }

    public void setBasedOnTermFields(Set<String> basedOnTermFields) {
        this.basedOnTermFields = basedOnTermFields;
    }

    public void setPlanName(String planName) {
        this.planName = planName;
    }

    public StaffSalaryCalcPeriod convert(StaffPayPeriodTermCalcContext calcContext) {
        personalCalcLog.clear();
        StaffSalaryCalcContext staffCalcContext = calcContext.getContext();
        this.staffPeriodSalary = staffCalcContext.getSpData();
        this.inputData = staffCalcContext.getInputData();
        this.attendanceMonthlyReport = staffCalcContext.getAtm();
        this.attendancePeriodReports = staffCalcContext.getAtps();
        this.referenceData = staffCalcContext.getRefData();
        this.basedOnTermSalary = staffCalcContext.getBasedOnTermSalary();
        ValidationUtils.rejectIfEmpty(staffPeriodSalary, null, String.format("员工[%s]薪资档案薪资为空", calcContext.getStaffName()));
        ValidationUtils.rejectIfEmpty(attendancePeriod, null, "薪资考勤周期未设定");

        StaffSalaryCalcPeriod convertedSalaryCalcPeriod = new StaffSalaryCalcPeriod();

        //不管分布不分段，输入数据只有一份
        if (CollectionUtils.isNotEmpty(inputData)) {
            //只需将工资表中的输入项目加入
            for (Map.Entry<String, DynamicCell> entry : inputData.entrySet()) {
                String code = entry.getKey();
                if (salaryFieldMap.containsKey(code)) {
                    convertedSalaryCalcPeriod.addSummarizedItem(code, entry.getValue());
                }
            }

        }
        //不管分不分段，员工的服务引用数据只有一份


        Map<String, List<SalaryField>> listMap = salaryFieldMap.values().stream()
                .filter(s -> FieldSource.FUNCTION.equals(s.getSource())
                        && StringUtils.isNotEmpty(s.getValueMeta().getFormula())
                        && s.getValueMeta().getFormula().startsWith("="))
                .collect(Collectors.groupingBy(s -> {
                    String formula = s.getValueMeta().getFormula();
                    String expression = formula.substring(1);
                    return expression;
                }));
        Map<String, Set<String>> salaryFormulaCodeMap = new HashMap<>(listMap.size());
        if (CollectionUtils.isNotEmpty(listMap)) {
            for (Map.Entry<String, List<SalaryField>> entry : listMap.entrySet()) {
                String formula = entry.getKey();
                List<SalaryField> salaryFields = entry.getValue();
                Set<String> codes = salaryFields.stream().map(s -> s.getCode()).collect(Collectors.toSet());
                salaryFormulaCodeMap.put(formula, codes);
            }
        }


        if (CollectionUtils.isNotEmpty(referenceData)) {
            for (Map.Entry<String, Map<String, Object>> entry : referenceData.entrySet()) {
                String serviceId = entry.getKey();
                Map<String, Object> serviceData = entry.getValue();
                //:TODO 考虑去除未被引用到但是被读取的字段
                //没有根据用户指定的数据类型进行进行赋值，直接根据Object的类型进行转换，可优化
                if (CollectionUtils.isNotEmpty(serviceData)) {
                    for (Map.Entry<String, Object> field : serviceData.entrySet()) {
                        String filedName = field.getKey();
                        Object fieldValue = field.getValue();
                        String variableID = VariableID.buildVariableId(serviceId, filedName);
                        if (fieldValue != null) {
                            if (salaryFormulaCodeMap.containsKey(variableID)) {
                                Set<String> codes = salaryFormulaCodeMap.get(variableID);
                                codes.forEach(s -> convertedSalaryCalcPeriod.addSummarizedItem(s, fieldValue.toString()));
                            }
                        } else {
                            if (salaryFormulaCodeMap.containsKey(variableID)) {
                                Set<String> codes = salaryFormulaCodeMap.get(variableID);
                                codes.forEach(s -> convertedSalaryCalcPeriod.addVoidSummarizedItem(s));
                            }
                        }
                    }
                }
            }
        }
        if (basedOnTermSalary != null) {
            //添加合并计税依赖的项目
            Map<String, DynamicCell> data = basedOnTermSalary.getData();
            if (CollectionUtils.isNotEmpty(data) && CollectionUtils.isNotEmpty(basedOnTermFields)) {
                for (String fieldName:basedOnTermFields){
                    DynamicCell fieldValue = data.get(fieldName);
                    String variableID = VariableID.buildVariableWithSheetIdAndFieldId(SalaryCalcJobConfig.BASED_ON_PLAN_PLACEHOLDER, fieldName);

                    if (fieldValue != null) {
                        if (salaryFormulaCodeMap.containsKey(variableID)) {
                            Set<String> codes = salaryFormulaCodeMap.get(variableID);
                            codes.forEach(s ->
                                            convertedSalaryCalcPeriod.addSummarizedItem(s, fieldValue)
                            );
                        }
                    } else {
                        if (salaryFormulaCodeMap.containsKey(variableID)) {
                            Set<String> codes = salaryFormulaCodeMap.get(variableID);
                            codes.forEach(s -> convertedSalaryCalcPeriod.addVoidSummarizedItem(s));
                        }
                    }
                }
            }
        }

        boolean isSinglePhase = staffPeriodSalary.isSinglePhase();

        boolean hasPhasedAttendanceReport = true;

        if (isSinglePhase) {
            hasPhasedAttendanceReport = false;
        }

        if (CollectionUtils.isEmpty(attendancePeriodReports)) {
            hasPhasedAttendanceReport = false;
        }


        //处理薪资档案相关的引用数据
        if (staffPeriodSalary.hasPhase()) {
            List<StaffSalaryCalcPeriodTerm> phases = staffPeriodSalary.sortedStaffSalaryCalcPeriodTerms();

            if (CollectionUtils.isNotEmpty(phases)) {
                convertedSalaryCalcPeriod.setPhases(phases);

                for (StaffSalaryCalcPeriodTerm term : phases) {
                    //开始设置薪资分段计算基础部分
                    if (isSinglePhase || !hasPhasedAttendanceReport) {
                        //step2 设置考勤月报
                        if (attendanceMonthlyReport != null) {
                            Map<String, Double> attendanceData = attendanceMonthlyReport.toDynamicCells();
                            if (CollectionUtils.isNotEmpty(attendanceData)) {
                                for (Map.Entry<String, Double> entry : attendanceData.entrySet()) {
                                    String code = entry.getKey();
                                    Double value = entry.getValue();
                                    String variableID = VariableID.buildVariableId(ServiceProvider.SERVICE_ID.ATTENDANCE_MONTHLY_REPORT, code);
                                    if (value != null) {
                                        if (salaryFormulaCodeMap.containsKey(variableID)) {
                                            Set<String> codes = salaryFormulaCodeMap.get(variableID);
                                            codes.forEach(s ->
                                                            convertedSalaryCalcPeriod.addSummarizedItem(s, new DynamicCell(value))
                                            );
                                        }
                                    } else {
                                        if (salaryFormulaCodeMap.containsKey(variableID)) {
                                            Set<String> codes = salaryFormulaCodeMap.get(variableID);
                                            codes.forEach(s -> convertedSalaryCalcPeriod.addVoidSummarizedItem(s));
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        //设置分段数据，如果分段数据没有，那么不需要分段计算考勤薪资部分
                        if (hasPhasedAttendanceReport) {
                            //设置当前分段的考勤数据,如果没有结束日期，默认结束时间为考勤周期结束时间
                            DateRange range = DateRange.build(term.getStartOn(), term.getEndOn() != null ? term.getEndOn() : attendancePeriod.getEnd());
                            /**
                             * | 标记的位置为分段点,1-31为薪资周期，26-25为考勤周期
                             *              1---P3----|-------------P2---------------|-------P1---------31
                             *   26-----------P3------|-------------P2---------------|---P1----25
                             *
                             *              1---P2---|------------------------------P1-------------------31
                             *   26----------P2------|------------------------------P1----------25
                             *
                             *  ---P2---|---1---------------------------------------P1-------------------31
                             *   26-----|-------------------------------------------P1---------25
                             */
                            //phases里面分段开始时间，是以考勤周期为基础的，这里的分段数据主要用于考勤相关薪资的分段计算。
                            Optional<AttendancePeriodReport> optional = attendancePeriodReports.stream().filter(s -> s.getDateRange().isSameSartDate(range)).findFirst();
                            if (optional.isPresent()) {
                                AttendancePeriodReport periodReport = optional.get();
                                Map<String, Double> periodAttendanceData = periodReport.toDynamicCells();
                                //保存分段考勤数据
                                Optional<StaffSalaryCalcPeriodTerm> optionalTerm = convertedSalaryCalcPeriod.getPhases().stream().filter(t -> t.getStartOn().equals(term.getStartOn()) && t.getEndOn().equals(term.getEndOn())).findFirst();

                                if (optionalTerm.isPresent()) {
                                    final StaffSalaryCalcPeriodTerm currentTerm = optionalTerm.get();
                                    for (Map.Entry<String, Double> entry : periodAttendanceData.entrySet()) {
                                        String code = entry.getKey();
                                        Double value = entry.getValue();
                                        String variableID = VariableID.buildVariableId(ServiceProvider.SERVICE_ID.ATTENDANCE_MONTHLY_REPORT, code);
                                        if (optionalTerm.isPresent()) {
                                            if (value != null) {
                                                if (salaryFormulaCodeMap.containsKey(variableID)) {
                                                    Set<String> codes = salaryFormulaCodeMap.get(variableID);
                                                    codes.forEach(s -> currentTerm.addItem(s, variableID.toString()));

                                                }
                                            } else {
                                                if (salaryFormulaCodeMap.containsKey(variableID)) {
                                                    Set<String> codes = salaryFormulaCodeMap.get(variableID);
                                                    codes.forEach(s -> currentTerm.addVoidItem(s));

                                                }
                                            }
                                        }
                                    }
                                }

                            } else {
                                logger.warn("未{}找到分段信息");
                            }
                        }
                    }
                }
                //不管几段，默认将最新一段的薪资作为汇总数据
                StaffSalaryCalcPeriodTerm latestStaffSalaryCalcPeriodTerm = phases.get(0);
                Map<String, DynamicCell> items = latestStaffSalaryCalcPeriodTerm.getItems();
                if (CollectionUtils.isNotEmpty(items)) {
                    for (Map.Entry<String, DynamicCell> entry : items.entrySet()) {
                        String code = entry.getKey();
                        DynamicCell value = entry.getValue();
                        if (salaryFieldMap.containsKey(code)) {
                            if (value == null) {
                                convertedSalaryCalcPeriod.addVoidSummarizedItem(code);
                            } else {
                                convertedSalaryCalcPeriod.addSummarizedItem(code, value);
                            }
                        }
                    }
                }
            }
        }
        convertedSalaryCalcPeriod.setSihfStatus(calcContext.getSihfStatus());
        convertedSalaryCalcPeriod.setAttendanceStatus(calcContext.getAttendanceStatus());
        return convertedSalaryCalcPeriod;
    }
}




